#include "header.hpp"
using namespace std;

// ※Callable object wrapper and binder
/* Callable object:
 * 1)function pointer
 * 2)the class overloads operator()
 * 3)the object can be converted to function pointer
 * 4)a pointer of member function or member variable
*/

void print1(int num, string name){
    cout << "id: " << num << ", name: " << name << endl;
}

void test6(){
    using funcptr = void(*)(int, string);

    class Test{
    public:
        void operator()(string msg){
            cout << msg << endl;
        }

        //Type convert function
        //1)No parameters, no return type
        //2)Must be a member function
        //3)Return target type variable
        operator funcptr(){
            //return hello; //error
            return &world;
        }

        void hello(int a, string s){
            cout << "number: " << a << ", name: " << s << endl;
        }

        static void world(int a, string s){
            cout << "number: " << a << ", name: " << s << endl;
        }

        int m_id = 520;
    };
    //end of class Test

    //1.callable object->function pointer
    funcptr fp = print1;
    fp(1, "alice");

    //2.callable object->the class overloads operator()
    Test t1;
    t1("The second condition");

    //3.callable object->the object can be converted to function pointer
    Test t2;
    t2(2, "chino");

    //4.callable object->a pointer of member function or member variable
    using mem_fn_ptr = void(Test::*)(int, string);
    mem_fn_ptr fptr = &Test::hello;
    Test t3;
    (t3.*fptr)(3, "karen");

    using mem_var_ptr = int Test::*;
    mem_var_ptr pv = &Test::m_id;
    Test t4;
    cout << (t4.*pv) << endl;


    cout << "--------------------------------------" << endl;


    //callable object wrapper
    //1)wrap a normal function
    function<void(int, string)> f1 = print1;

    //2)wrap a static function in class
    function<void(int, string)> f2 = &Test::world;

    //3)wrap functor
    Test t5;
    function<void(string)> f3 = t5;

    //4)wrap the object convert to function pointer
    Test t6;
    function<void(int, string)> f4 = t6;

    //Invoke
    f1(1, "natsu");
    f2(2, "reisa");
    f3("hinata");
    f4(3, "airi");


    cout << "--------------------------------------" << endl;


    class A{
    public:
        A(const function<void(int, string)>& f)
        : callback(f){}

        void notify(int id, string name){
            callback(id, name);
        }

    private:
        function<void(int, string)> callback;
    };


    A a1(print1);
    a1.notify(1, "alice");

    A a2(Test::world);
    a2.notify(2, "chino");

    A a3(t6);
    a3.notify(3, "airi");
}
